package physics;

public class PolygonCollisions {
	// Structure that stores the results of the PolygonCollision function
			public class PolygonCollisionResult {
				public boolean willIntersect; // Are the polygons going to intersect forward in time?
				public boolean intersect; // Are the polygons currently intersecting
				public Vector minimumTranslationVector; // The translation to apply to polygon A to push the polygons appart.
			}

			// Check if polygon A is going to collide with polygon B for the given velocity
			public PolygonCollisionResult PolygonCollision(Polygon polygonA, Polygon polygonB, Vector velocity) {
				boolean intersect = false;
				PolygonCollisionResult result = new PolygonCollisionResult();
				result.intersect = true;
				result.willIntersect = true;

				int edgeCountA = polygonA.edges.size();
				int edgeCountB = polygonB.edges.size();
				double minIntervalDistance = 500000000;
				Vector translationAxis = new Vector();
				Vector edge;

				// Loop through all the edges of both polygons
				for (int edgeIndex = 0; edgeIndex < edgeCountA + edgeCountB; edgeIndex++) {
					if (edgeIndex < edgeCountA) {
						edge = polygonA.edges.get(edgeIndex);
					} else {
						edge = polygonB.edges.get(edgeIndex - edgeCountA);
					}

					// ===== 1. Find if the polygons are currently intersecting =====

					// Find the axis perpendicular to the current edge
					Vector axis = new Vector(-edge.y, edge.x);
					axis = axis.normalize();

					// Find the projection of the polygon on the current axis
					double minA = 0;
					double minB = 0; 
					double maxA = 0; 
					double maxB = 0;
					double[] minmaxA = ProjectPolygon(axis, polygonA, minA, maxA);
					minA = minmaxA[0];
					maxA = minmaxA[1];
					double[] minmaxB = ProjectPolygon(axis, polygonB, minB, maxB);
					minB = minmaxB[0];
					maxB = minmaxB[1];

					// Check if the polygon projections are currentlty intersecting
					if (IntervalDistance(minA, maxA, minB, maxB) > 0) 
						intersect = false;

					// ===== 2. Now find if the polygons *will* intersect =====

					// Project the velocity on the current axis
					double velocityProjection = axis.dot(velocity);

					// Get the projection of polygon A during the movement
					if (velocityProjection < 0) {
						minA += velocityProjection;
					} else {
						maxA += velocityProjection;
					}

					// Do the same test as above for the new projection
					double intervalDistance = IntervalDistance(minA, maxA, minB, maxB);
					if (intervalDistance > 0) 
						intersect = false;

					// If the polygons are not intersecting and won't intersect, exit the loop
					if (!result.intersect && !result.willIntersect) 
						break;

					// Check if the current interval distance is the minimum one. 
					// If so store the interval distance and the current distance.
					// This will be used to calculate the minimum translation vector
					intervalDistance = Math.abs(intervalDistance);
					if (intervalDistance < minIntervalDistance) {
						minIntervalDistance = intervalDistance;
						translationAxis = axis;

						Vector d = polygonA.posn.add(polygonB.posn.multiply(-1));
						if (d.dot(translationAxis) < 0) 
							translationAxis = translationAxis.multiply(-1);
					}
				}

				// The minimum translation vector can be used to push 
				// the polygons appart.
				// First moves the polygons by their velocity
				// then move polygonA by MinimumTranslationVector.
				if (result.willIntersect) 
					result.minimumTranslationVector = 
					translationAxis.multiply(minIntervalDistance);
				
				return result;
			}

			// Calculate the distance between [minA, maxA] and [minB, maxB]
			// The distance will be negative if the intervals overlap
			public double IntervalDistance(double minA, double maxA, 
					double minB, double maxB) {
				if (minA < minB) {
					return minB - maxA;
				} else {
					return minA - maxB;
				}
			}

			// Calculate the projection of a polygon on an axis and returns 
			//it as a [min, max] interval
			public double[] ProjectPolygon(Vector axis, Polygon polygon, 
					double min, double max) {
				// To project a point on an axis use the dot product
				double[] minmax = new double[2];
				double d = axis.dot(polygon.points.get(0));
				min = d;
				max = d;
				for (int i = 0; i < polygon.points.size(); i++) {
					d = polygon.points.get(i).dot(axis);
					if (d < min) {
						min = d;
					} else {
						if (d > max) {
							max = d;
						}
					}
				}
				minmax[0] = min;
				minmax[1] = max;
				return minmax;
			}

}
